// APIs
var zScrollDown = function(_target_node_id, step, callback) {
	var target_node = document.getElementById(_target_node_id);
	var scroll_step = Number(step) * -1;

	var ez = fxEngine.select("scrolly")
	ez.targetNode(target_node);
	ez.regCallback(callback);
	ez.moveY(scroll_step);
};

var zScrollUp = function(_target_node_id, step, callback) {
	var target_node = document.getElementById(_target_node_id);
	var scroll_step = Number(step);

	var ez = fxEngine.select("scrolly")
	ez.targetNode(target_node);
	ez.regCallback(callback);
	ez.moveY(scroll_step);
};

var zScrollRight = function(_target_node_id, step, callback) {
	var target_node = document.getElementById(_target_node_id);
	var scroll_step = Number(step) * -1;

	var ez = fxEngine.select("scrollx")
	ez.targetNode(target_node);
	ez.regCallback(callback);
	ez.moveX(scroll_step);
};

var zScrollLeft = function(_target_node_id, step, callback) {
	var target_node = document.getElementById(_target_node_id);
	var scroll_step = Number(step);

	var ez = fxEngine.select("scrollx")
	ez.targetNode(target_node);
	ez.regCallback(callback);
	ez.moveX(scroll_step);
};

//
var zScrollTop = function(_target_node_id) {
	var target_node = document.getElementById(_target_node_id);

	var ez = fxEngine.select("scrolly")
	ez.targetNode(target_node);
	ez.moveY("", "0");
}

var zBlink = function(_target_node, _duration, callback) {
	fadeOut(_target_node, _duration, Bind(this, function() {
		fadeIn(_target_node, _duration)
	}, _target_node, _duration));
}

var fadeIn = function(_target_node, _duration, callback) {
	var target_node = document.getElementById(_target_node);
	var duration = _duration || 1

	var step = 0.01;

	var ez = fxEngine.select("fade")
	ez.targetNode(target_node);
	ez.regCallback(callback);
	ez.fade(step, duration);
}

var fadeOut = function(_target_node, _duration, callback) {
	var target_node = document.getElementById(_target_node);
	var duration = _duration || 1

	var step = -0.01;

	var ez = fxEngine.select("fade")
	ez.targetNode(target_node);
	ez.regCallback(callback);
	ez.fade(step, duration);
}



var zEngines = function() {
	this.engines = {};
	this.engines["scrolly"] = new zEffect();
	this.engines["scrollx"] = new zEffect();
	this.engines["fade"] = new zEffect();
};

zEngines.prototype.select = function(type) {
	return this.engines[type];
};



var zEffect = function() {
	this.tic = 0;
	this.running = false;
	this.action_index = 0;
};

zEffect.prototype.targetNode = function(target_node) {
	this.target = target_node;
};

zEffect.prototype.regCallback = function(callback) {
	this.callback = callback;
};

//
// effect enterances
zEffect.prototype.moveX = function(step, end_x) {
	// Every event runner have to pass below 2 methods.
	if(this.running) return;
	if(this.time_worker) this.__stop();

	this.end_x = (step) ? this.current_left() + step : end_x;
	if(this.end_x > MAX_X || (MIN_X && this.end_x <= MIN_X)) return;
	// if(this.end_x > 0) return;

	this.actions = ["__moveX"];

	this.time_worker = this.timer().setInterval(Bind(this, this.__roll), 50);
};

zEffect.prototype.moveY = function(step, end_y) {
	// Every event runner have to pass below 2 methods.
	if(this.running) return;
	if(this.time_worker) this.__stop();

	this.end_y = (step) ? this.current_top() + step : end_y;
	if(this.end_y > MAX_Y || (MIN_Y && this.end_y <= MIN_Y)) return;
	// if(this.end_y > 0) return;

	this.actions = ["__moveY"];

	this.time_worker = this.timer().setInterval(Bind(this, this.__roll), 50);
};

zEffect.prototype.fade = function(step, duration) {
	// Every event runner have to pass below 2 methods.
	if(this.running) return;
	if(this.time_worker) this.__stop();

	this.end_o = (step > 0) ? 1 : 0;
	this.step = step;

	this.actions = ["__fade"];

	var interval = (duration * 1000 / (1 / Math.abs(step)));
	this.time_worker = this.timer().setInterval(Bind(this, this.__roll), interval);
};

// 
// main
zEffect.prototype.__roll = function() {
	this.running = true;
	try {
		if(this.tic == 0 || !this[this.actions[this.action_index] + "EndCondition"]()) {
			this[this.actions[this.action_index]]();
			this.tic++;
		}
		else if(typeof(this[this.actions[this.action_index + 1]]) == "function") {
			this.tic = 0;
			this.action_index = this.action_index + 1;
			this[this.actions[this.action_index]]();
		}
		else {
			this[this.actions[this.action_index] + "Finalize"]();
		}
	} catch(e) {
		// Log(e);
		this.__stop();
	}

};

//
// effects
//
// moveX
zEffect.prototype.__moveX = function() {
	var active_num = (this.tic == 0)? 0 : ((this.tic * 0.9) / this.tic);
	var action_num = ((this.end_x - this.current_left()) * active_num);
	// if(action_num > Math.abs(this.end_y)) this.__moveYFinalize();
	this.target.style.left = this.current_left() + action_num + "px"
};
zEffect.prototype.__moveXEndCondition = function() {
	return Math.floor(Math.abs(this.end_x - this.current_left())) <= 1;
};
zEffect.prototype.__moveXFinalize = function() {
	this.target.style.left = this.end_x + "px";
	this.__stop();
};
//
// moveY
zEffect.prototype.__moveY = function() {
	var active_num = (this.tic == 0)? 0 : ((this.tic * 0.9) / this.tic);
	var action_num = ((this.end_y - this.current_top()) * active_num);
	// if(action_num > Math.abs(this.end_y)) this.__moveYFinalize();
	this.target.style.top = this.current_top() + action_num + "px"
};
zEffect.prototype.__moveYEndCondition = function() {
	return Math.floor(Math.abs(this.end_y - this.current_top())) <= 1;
};
zEffect.prototype.__moveYFinalize = function() {
	this.target.style.top = this.end_y + "px";
	this.__stop();
};

// 
// fade
zEffect.prototype.__changeOpacity = function(opacity) {
	this.target.style.MozOpacity = opacity;
	this.target.style.filter = "alpha(opacity=" + opacity * 100 + ")";
	this.target.style.opacity = opacity;
};

zEffect.prototype.__fade = function() {
	if(this.tic == 0) this.__changeOpacity((this.end_o == 1) ? 0 : 1);
	this.__changeOpacity(this.current_opacity() + this.step);
};
zEffect.prototype.__fadeEndCondition = function() {
	return Math.abs(this.end_o - this.current_opacity()) < Math.abs(this.step);
	// return Math.floor(this.current_opacity()) == this.end_o;
};
zEffect.prototype.__fadeFinalize = function() {
	this.__changeOpacity(this.end_o);
	this.__stop();
};

//
// fetch target attributes
zEffect.prototype.current_left = function() {
	return Number(this.target.style.left.replace(/px$/, ''));
};

zEffect.prototype.current_top = function() {
	return Number(this.target.style.top.replace(/px$/, ''));
};

zEffect.prototype.current_opacity = function() {
	var __o = this.target.style.MozOpacity || 0;
	return Number(__o);
};

// 
//
zEffect.prototype.__stop = function() {
	this.timer().clearInterval(this.time_worker);
	this.tic = 0;
	this.running = false;
	this.action_index= 0;
	this.time_worker = null;

	if(this.callback) {
		this.callback();
		this.callback = null;
	}

};

zEffect.prototype.timer = function() {
	if(!this.factory_timer) this.factory_timer = window;

	return this.factory_timer;
};
